﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using com.nm.Helper;
using StackExchange.Redis;

// ReSharper disable once CheckNamespace
namespace com.nm {
	/// <summary>
	/// Redis客户端
	/// </summary>
	public class RedisClient : IDisposable {
		private static ConfigurationOptions _configuration;
		/// <summary>
		/// 从Web.config中获取Redis配置信息
		/// </summary>
		protected static ConfigurationOptions Configuration {
			get {
				//var server = ConfigurationHelper.Get("redis-server", "127.0.0.1");
				//var port = ConfigurationHelper.Get("redis-server-port", 6379);
				//var pass = ConfigurationHelper.Get("redis-server-pass", "1QAZ2wsxheyue");
				var server = ConfigurationHelper.Get("redis-server", "127.0.0.1");
				var port = ConfigurationHelper.Get("redis-server-port", 6379);
				var pass = ConfigurationHelper.Get("redis-server-pass", "1qazxcvfr4");
				return _configuration ?? (
				_configuration = new ConfigurationOptions() {
					AbortOnConnectFail = false,
					ConnectTimeout = 1000,
					ResponseTimeout = 1000,
					EndPoints = { { server, port } },
					Password = pass
				});
			}

		}
		private static readonly Lazy<ConnectionMultiplexer> LazyConnection = new Lazy<ConnectionMultiplexer>(() => {
			return ConnectionMultiplexer.Connect(Configuration);
		});
		/// <summary>
		/// 创建一个Redis连接
		/// </summary>
		public static ConnectionMultiplexer Connection {
			get {
				return LazyConnection.Value;
			}
		}
		/// <summary>
		/// 返回一个Reids服务器
		/// </summary>
		public static IServer Server {
			get {
				var ep = Connection.GetEndPoints(true);
				return Connection.GetServer(ep.FirstOrDefault());
			}
		}

		public int DataBaseIndex {
			get;
			protected set;
		}
		private IDatabase _database;
		/// <summary>
		/// 返回一个数据库
		/// </summary>
		/// <returns></returns>
		public IDatabase GetDatabase() {
			return _database ?? (_database = Connection.GetDatabase(this.DataBaseIndex));
		}
		/// <summary>
		/// 初始化客户端
		/// </summary>
		/// <param name="index">要打开的数据库（0-15），默认-1，超出有效访问自动修改为-1</param>
		public RedisClient(int index = -1) {
			this.DataBaseIndex = Math.Max(Math.Min(index, 15), -1);
		}

		#region Redis 基本数据的读写操作
		/// <summary>
		/// 添加一个字符串到Redis
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		/// <param name="minutes"></param>
		public RedisClient AddString(string key, string value, int minutes = 0) {
			var db = this.GetDatabase();
			if (minutes > 0)
				db.StringSet(key, value, TimeSpan.FromMinutes(minutes));
			else
				db.StringSet(key, value);
			return this;
		}
		/// <summary>
		/// 添加一个字符串到Redis
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		/// <param name="minutes"></param>
		public RedisClient AddLong(string key, long value, int minutes = 0) {
			var db = this.GetDatabase();
			if (minutes > 0)
				db.StringSet(key, value, TimeSpan.FromMinutes(minutes));
			else
				db.StringSet(key, value);
			return this;
		}
		#endregion


		public void SetIntList(Dictionary<string, int> list) {
			var db = this.GetDatabase();
			foreach (var key in list.Keys) {
				db.StringSet(key, list[key]);
			}
		}
		public void SetInt(string key, int value) {
			var db = this.GetDatabase();
			db.StringSet(key, value);
		}
		public RedisClient SetString(string key, RedisValue value, int minutes) {
			var db = this.GetDatabase();
			db.StringSet(key, value, TimeSpan.FromMinutes(minutes));
			return this;
		}
		public RedisClient SetString(string key, RedisValue value, TimeSpan timeSpan) {
			var db = this.GetDatabase();
			db.StringSet(key, value, timeSpan);
			return this;
		}
		public RedisClient SetString(string key, RedisValue value) {
			var db = this.GetDatabase();
			db.StringSet(key, value);
			return this;
		}
		/// <summary>
		/// 添加字符串到SET
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		/// <returns></returns>
		public RedisClient AddStringToSet(string key, string value) {
			var db = this.GetDatabase();
			db.SetAdd(key, value);
			return this;
		}
		public RedisClient SetDecimal(string key, decimal value) {
			var db = this.GetDatabase();
			db.StringSet(key, value.ToString());
			return this;
		}
		public RedisClient SetStrString(string key, string value) {
			var db = this.GetDatabase();
			db.StringSet(key, value);
			return this;
		}
		public RedisClient SetString(string key, DateTime value) {
			var db = this.GetDatabase();
			db.StringSet(key, value.Ticks);
			return this;
		}
		public RedisClient SetString(string key, DateTime value, int minutes) {
			var db = this.GetDatabase();
			db.StringSet(key, value.Ticks, TimeSpan.FromMinutes(minutes));
			return this;
		}
		/// <summary>
		/// 重置超时时间
		/// </summary>
		/// <param name="key"></param>
		/// <param name="minutes"></param>
		public RedisClient Expire(string key, int minutes) {
			var db = this.GetDatabase();
			db.KeyExpire(key, TimeSpan.FromMinutes(minutes));
			return this;
		}
		/// <summary>
		/// 判断 item 是否存在SET（key）中
		/// </summary>
		/// <param name="key"></param>
		/// <param name="item"></param>
		/// <returns></returns>
		public bool IsItemExists(string key, RedisValue item) {
			var db = this.GetDatabase();
			return db.SetContains(key, item);
		}
		/// <summary>
		/// 判断 item 是否存在SET（key）中
		/// </summary>
		/// <param name="key"></param>
		/// <param name="item"></param>
		/// <returns></returns>
		public bool IsIntInSet(string key, int item) {
			return IsMember(key, item);
		}
		/// <summary>
		/// 判断 item 是否存在SET（key）中
		/// </summary>
		/// <param name="key"></param>
		/// <param name="item"></param>
		/// <returns></returns>
		public bool IsStringSet(string key, string item) {
			return IsMember(key, item);
		}

		/// <summary>
		/// 从Redis中读取 二进制数据（byte[]）
		/// </summary>
		/// <param name="key"></param>
		/// <param name="dv"></param>
		/// <returns></returns>
		public byte[] GetBuffer(string key, byte[] dv) {
			var db = this.GetDatabase();
			return db.KeyExists(key)
				? (byte[])db.StringGet(key)
				: dv;
		}
		public RedisValue GetString(string key, RedisValue dv) {
			var db = this.GetDatabase();
			return db.KeyExists(key) ? db.StringGet(key) : dv;
		}
		public RedisValue GetString(string key) {
			var db = this.GetDatabase();
			return db.StringGet(key);
		}
		/// <summary>
		/// 从Redis中读取一个字符串
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		public string GetStringValue(string key) {
			var db = this.GetDatabase();
			return db.StringGet(key);
		}
		/// <summary>
		/// 判断Key是否存在
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		public bool KeyExists(string key) {
			var db = this.GetDatabase();
			return db.KeyExists(key);
		}
		public int GetInt(string key, int dv = 0) {
			var db = this.GetDatabase();
			if (!db.KeyExists(key)) {
				return dv;
			}
			var r = db.StringGet(key);
			if (!r.HasValue) {
				return dv;
			}
			var i = 0;
			r.TryParse(out i);
			return i;
		}

		public double GetDouble(string key, double dv = 0) {
			var db = this.GetDatabase();
			if (!db.KeyExists(key)) {
				return dv;
			}
			var r = db.StringGet(key);
			if (!r.HasValue) {
				return dv;
			}
			var i = 0D;
			r.TryParse(out i);
			return i;
		}

		public long GetLong(string key, long dv = 0) {
			var db = this.GetDatabase();
			if (!db.KeyExists(key)) {
				return dv;
			}
			var r = db.StringGet(key);
			if (!r.HasValue) {
				return dv;
			}
			var i = 0L;
			r.TryParse(out i);
			return i;
		}
		public decimal GetDecimal(string key, decimal dv = 0) {
			var db = this.GetDatabase();
			if (!db.KeyExists(key)) {
				return dv;
			}
			var r = db.StringGet(key);
			if (!r.HasValue) {
				return dv;
			}
			var i = 0M;
			if (decimal.TryParse(r.ToString(), out i)) {
				return i;
			}
			return dv;
		}
		//

		public int GetAndMinuInt(string key, int dv = 0) {
			var db = this.GetDatabase();
			if (!db.KeyExists(key)) {
				return dv;
			}
			var r = db.StringGet(key);
			if (!r.HasValue) {
				return dv;
			}
			var i = 0;
			if (r.TryParse(out i))
				db.StringSet(key, i - 1);
			return i;

		}

		/// <summary>
		/// 向一个LIST添加数据
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		/// <returns></returns>
		public long AddValueToList(string key, RedisValue value) {
			var db = this.GetDatabase();
			return db.ListRightPush(key, value);
		}
		/// <summary>
		/// 向一个LIST添加字符串
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		/// <returns></returns>
		public long AddStringToList(string key, string value) {
			var db = this.GetDatabase();
			return db.ListRightPush(key, value);
		}
		/// <summary>
		/// 添加Int到List
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		/// <returns></returns>
		public long AddIntToList(string key, int value) {
			var db = this.GetDatabase();
			return db.ListRightPush(key, value);
		}
		/// <summary>
		/// 批量添加Int到List
		/// </summary>
		/// <param name="key"></param>
		/// <param name="values"></param>
		/// <returns></returns>
		public long AddIntToList(string key, IEnumerable<int> values) {
			var db = this.GetDatabase();
			return db.ListRightPush(key, values.Select(i => (RedisValue)i).ToArray());
		}

		/// <summary>
		/// 获取LIST
		/// </summary>
		/// <param name="key"></param>
		/// <param name="start"></param>
		/// <param name="stop"></param>
		/// <returns></returns>
		public RedisValue[] GetList(string key, long start = 0L, long stop = -1L) {
			var db = this.GetDatabase();
			return db.ListRange(key, start, stop);
		}
		/// <summary>
		/// 从List中读取一个Int值
		/// </summary>
		/// <param name="key"></param>
		/// <param name="index"></param>
		/// <returns></returns>
		public int GetIntFromList(string key, int index) {
			var db = this.GetDatabase();
			return (int)db.ListGetByIndex(key, index);
		}
		/// <summary>
		/// 修改 List 中 的数据
		/// </summary>
		/// <param name="key"></param>
		/// <param name="index"></param>
		/// <param name="value"></param>
		/// <returns></returns>
		public RedisClient SetIntToList(string key, int index, int value) {
			var db = this.GetDatabase();
			db.ListSetByIndex(key, index, 0);
			return this;
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="key"></param>
		/// <param name="start"></param>
		/// <param name="stop"></param>
		/// <returns></returns>
		public IList<string> GetStringFromList(string key, long start = 0L, long stop = -1L) {
			var db = this.GetDatabase();
			return db.ListRange(key, start, stop).Select(s => s.ToString()).ToList();
		}

		/// <summary>
		/// 返回一个LIST的“长度”
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		public long GetListLength(string key) {
			var db = this.GetDatabase();
			return db.ListLength(key);
		}
		/// <summary>
		/// 返回一个SET的“长度”
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		public long GetSetLength(string key) {
			var db = this.GetDatabase();
			return db.SetLength(key);
		}

		/// <summary>
		/// 统计关键字（Key）的数量
		/// </summary>
		/// <param name="pattern"></param>
		/// <returns></returns>
		public long Count(string pattern) {
			long result = 0;
			var server = Server;
			IScanningCursor cursor;
			var keys = server.Keys(DataBaseIndex, pattern, 1000).ToList();
			do {
				result += keys.LongCount();
				cursor = keys as IScanningCursor;
				if (cursor != null) {
					keys = server.Keys(DataBaseIndex, pattern, cursor.PageSize, cursor.Cursor, cursor.PageOffset).ToList();
				}
			} while (cursor != null && cursor.Cursor > 0 && keys.LongCount() > 0);
			return result;
		}

		public IEnumerable<string> FindKey(string pattern, int count) {
			var server = Server;
			if (count < 1000)
				return server.Keys(DataBaseIndex, pattern).Select(i => i.ToString());
			var result = new List<string>();
			IScanningCursor cursor;
			var keys = server.Keys(DataBaseIndex, pattern, 1000)
				.Select(i => i.ToString());
			do {
				result.AddRange(keys);
				cursor = keys as IScanningCursor;
				if (cursor != null) {
					keys = server.Keys(DataBaseIndex, pattern, cursor.PageSize, cursor.Cursor, cursor.PageOffset)
						.Select(i => i.ToString());
				}
			} while (cursor != null && cursor.Cursor > 0);
			return result;
		}
		public IEnumerable<RedisKey> GetKeys(string pattern, int count) {
			var server = Server;
			if (count < 1000) return server.Keys(DataBaseIndex, pattern);
			var result = new List<RedisKey>();
			IScanningCursor cursor;
			var keys = server.Keys(DataBaseIndex, pattern, 1000);
			do {
				result.AddRange(keys);
				cursor = keys as IScanningCursor;
				if (cursor != null) {
					keys = server.Keys(DataBaseIndex, pattern, cursor.PageSize, cursor.Cursor, cursor.PageOffset);
				}
			} while (cursor != null && cursor.Cursor > 0);
			return result;
		}
		/// <summary>
		/// 搜索关键字
		/// </summary>
		/// <param name="pattern"></param>
		/// <param name="page"></param>
		/// <param name="limit"></param>
		/// <returns></returns>
		public IEnumerable<RedisKey> GetKeys(string pattern, int page, int limit) {
			var server = Server;
			var keys = server.Keys(DataBaseIndex, pattern, limit, 0, page);
			return keys;
		}

		#region LIST
		public long RemoveStringFromList(string pKey, string pValue) {
			var db = this.GetDatabase();
			return db.ListRemove(pKey, pValue);
		}
		#endregion

		#region Hash

		public RedisClient HashSet(string hashKey, string field, RedisValue value) {
			var db = this.GetDatabase();
			db.HashSet(hashKey, field, value);
			return this;
		}

		public RedisValue HashGet(string hashKey, string field, RedisValue dv) {
			if (!KeyExists(hashKey)) return dv;
			var db = this.GetDatabase();
			if (!db.HashExists(hashKey, field)) return dv;
			return db.HashGet(hashKey, field);
		}
		public RedisValue HashGet(string hashKey, int field, RedisValue dv) {
			if (!KeyExists(hashKey)) return dv;
			var db = this.GetDatabase();
			if (!db.HashExists(hashKey, field)) return dv;
			return db.HashGet(hashKey, field);
		}
		#endregion

		#region 集合（SET）

		public IEnumerable<RedisValue> GetValuesFromSet(string key, int skip, int limit) {
			var db = this.GetDatabase();
			if (!KeyExists(key)) throw new Exception(key + " 不存在");
			if (db.KeyType(key) != RedisType.Set) {
				throw new Exception(key + " 已经存在，当不是SET");
			}
			return db.SetScan(key, pageSize: limit, cursor: skip);
		}
		/// <summary>
		/// 从SET读取字符串，返回列表
		/// </summary>
		/// <param name="key"></param>
		/// <param name="skip"></param>
		/// <param name="limit"></param>
		/// <returns></returns>
		public IEnumerable<string> GetStringFromSet(string key, int skip, int limit) {
			var db = this.GetDatabase();
			if (!KeyExists(key)) throw new Exception(key + " 不存在");
			if (db.KeyType(key) != RedisType.Set) {
				throw new Exception(key + " 已经存在，当不是SET");
			}
			return db.SetScan(key, pageSize: limit, cursor: skip).Select(i => (string)i);
		}
		/// <summary>
		/// 从SET中读取 Int 列表
		/// </summary>
		/// <param name="key"></param>
		/// <param name="skip"></param>
		/// <param name="limit"></param>
		/// <returns></returns>
		public IEnumerable<int> GetIntFromSet(string key, int skip, int limit) {
			var db = this.GetDatabase();
			if (!KeyExists(key)) throw new Exception(key + " 不存在");
			if (db.KeyType(key) != RedisType.Set) {
				throw new Exception(key + " 已经存在，当不是SET");
			}
			return db.SetScan(key, pageSize: limit, cursor: skip).Select(i => (int)i);
		}

		/// <summary>
		/// 把一个值从集合中移除
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		public RedisClient RemoveValueFromSet(string key, RedisValue value) {
			var db = this.GetDatabase();
			if (!KeyExists(key))
				return this;
			if (db.KeyType(key) != RedisType.Set) {
				throw new Exception(key + " 已经存在，当不是SET");
			}
			db.SetRemove(key, value);
			return this;
		}
		/// <summary>
		/// 把一个值从集合中移除
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		public RedisClient RemoveStringFromSet(string key, string value) {
			return RemoveValueFromSet(key, value);
		}

		/// <summary>
		/// 把一个 Int 值从集合中移除
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		public RedisClient RemoveIntFromSet(string key, int value) {
			return RemoveValueFromSet(key, value);
		}
		/// <summary>
		/// 添加值到一个集合
		/// </summary>
		/// <param name="key">
		/// 如果数据库已经存在这个key但是不是SET类型则报错
		/// </param>
		/// <param name="value"></param>
		public void AddValueToSet(string key, RedisValue value) {
			var db = this.GetDatabase();
			if (KeyExists(key)) {
				if (db.KeyType(key) != RedisType.Set) {
					throw new Exception(key + " 已经存在，且不是SET");
				}
			}
			db.SetAdd(key, value);
		}
		/// <summary>
		/// 添加值到一个集合
		/// </summary>
		/// <param name="key">
		/// 如果数据库已经存在这个key但是不是SET类型则报错
		/// </param>
		/// <param name="value"></param>
		public void AddIntToSet(string key, int value) {
			var db = this.GetDatabase();
			if (KeyExists(key)) {
				if (db.KeyType(key) != RedisType.Set) {
					throw new Exception(key + " 已经存在，且不是SET");
				}
			}
			db.SetAdd(key, value);
		}
		/// <summary>
		/// 判断一个值是否包含在一个集合里面
		/// </summary>
		/// <param name="key">
		/// 如果给定的key不是一个SET报错
		/// </param>
		/// <param name="value"></param>
		/// <returns></returns>
		public bool IsMember(string key, RedisValue value) {
			var db = GetDatabase();
			if (!KeyExists(key))
				return false;
			if (db.KeyType(key) != RedisType.Set) {
				return false;
			}
			return db.SetContains(key, value);
		}

		#endregion

		#region Remove
		/// <summary>
		/// 根据匹配模式删除KEY
		/// </summary>
		/// <param name="pattern"></param>
		public long RemoveKeysByPattern(string pattern) {
			var ids = Server.Keys(this.DataBaseIndex, pattern);
			var db = this.GetDatabase();
			return db.KeyDelete(ids.ToArray());
		}

		/// <summary>
		/// 从数据库中删除值（按key来删除）
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		public bool RemoveKey(string key) {
			var db = this.GetDatabase();
			return db.KeyDelete(key);
		}
		/// <summary>
		/// 从数据库中删除一组key，不存在的key自动忽略
		/// </summary>
		/// <param name="keys"></param>
		public void RemoveKeys(IEnumerable<string> keys) {
			var db = this.GetDatabase();
			var ids = keys.Select(s => (RedisKey)s).ToArray();
			db.KeyDelete(ids);
		}
		/// <summary>
		/// 从数据库中删除一组key，不存在的key自动忽略
		/// </summary>
		/// <param name="keys"></param>
		public void RemoveKeys(IEnumerable<RedisKey> keys) {
			var db = this.GetDatabase();
			db.KeyDelete(keys.ToArray());
		}
		/// <summary>
		/// 从数据库中删除一组key，不存在的key自动忽略
		/// </summary>
		/// <param name="keys"></param>
		public void RemoveKeys(RedisKey[] keys) {
			var db = this.GetDatabase();
			db.KeyDelete(keys);
		}
		#endregion

		public long MinusIncrement(string key, int pValue) {
			var db = GetDatabase();
			var r = db.StringDecrement(key, pValue);
			return r;
		}

		public long PlusIncrement(string key, int pValue) {
			var db = GetDatabase();
			var r = db.StringIncrement(key, pValue);
			return r;
		}

		public object Test() {
			var db = this.GetDatabase();
			if (db.KeyExists("110_1_2")) {
				var r = db.StringGet("110_1_2");
				if (r.HasValue) {
					var i = 0;
					r.TryParse(out i);
					i++;
					db.StringSet("110_1_2", i);
					return i;
				}
				return false;
			}
			return db.StringSet("110_1_2", 134);
		}

		public void Dispose() {
			_configuration = null;
			_database = null;
		}


	}
}
